var editor
var flows
var codeMirrorInstances = {}
var socket
var version = 'lastest'

// function renderCodeMirror() {
//   codeMirrorInstances = {}
//   $('[codemirror]').each(function() {
//     var $dom = $(this)
//     if ($dom.attr('rendered') === 'true') {
//       return
//     }
//     $dom.attr('rendered', true)
//     var cm = CodeMirror(this, {
//       mode: 'spiderflow',	// 语法
//       theme: 'idea',	// 设置样式
//       placeholder: $dom.attr('placeholder'),
//       value: $dom.attr('data-value') || '',
//       scrollbarStyle: 'null'	// 隐藏滚动条
//     })
//     initHint(cm)
//     codeMirrorInstances[$(this).attr('codemirror')] = cm
//     cm.on('change', function() {
//       $dom.attr('data-value', cm.getValue())
//       if ($dom.attr('codemirror') === 'condition') {
//         var $select = $('select[name="exception-flow"]')
//         $select.siblings('div.layui-form-select').find('dl dd[lay-value=' + $select.val() + ']').click()
//       }
//       serializeForm()
//     })
//     codeMirrorInstances[$(this).attr('codemirror')] = cm
//   })
// }

function getCellData(cellId, keys) {
  var cell = editor.getModel().getCell(cellId)
  var data = []
  var object = cell.data.object
  for (var k in keys) {
    var key = keys[k]
    if (Array.isArray(object[key])) {
      var array = object[key]
      for (let i = 0, len = array.length; i < len; i++) {
        data[i] = data[i] || {}
        data[i][key] = array[i]
      }
    }
  }
  return data
}

// TODO:
// function serializeForm() {
//   var $container = $('.properties-container')
//   var _version = $container.data('version')
//   if (_version && _version !== version) {
//     return
//   }
//   var cellId = $container.attr('data-cellid')
//   var model = editor.getModel()
//   var cell = model.getCell(cellId)
//   if (!cell) {
//     return
//   }
//   var shape = cell.data.get('shape')
//   cell.data.reset({})
//   $.each($('.properties-container form').serializeArray(), function(index, item) {
//     var name = item.name
//     var value = item.value
//     if ($('.properties-container form *[name=' + name + '].array').length > 0) {
//       var array = cell.data.get(name) || []
//       array.push(value)
//       cell.data.set(name, array)
//     } else {
//       if (name == 'value') {
//         if (cell.getValue() != value) {
//           model.beginUpdate()
//           try {
//             cell.setValue(value)
//             model.execute(new mxValueChange(model, cell, value))
//           } finally {
//             model.endUpdate()
//           }
//         }
//       }
//       if (name == 'lineWidth') {
//         editor.graph.setCellStyles('strokeWidth', value, [cell])
//       }
//       if (name == 'line-style') {
//         editor.graph.setCellStyles('sharp', undefined, [cell])
//         editor.graph.setCellStyles('rounded', undefined, [cell])
//         editor.graph.setCellStyles('curved', undefined, [cell])
//         editor.graph.setCellStyles(value, 1, [cell])
//       }
//       cell.data.set(name, value)
//     }
//   })
//   $('.properties-container form [codemirror]').each(function() {
//     var $dom = $(this)
//     var name = $dom.attr('codemirror')
//     var value = $dom.attr('data-value')
//     if ($dom.hasClass('array')) {
//       var array = cell.data.get(name) || []
//       array.push(value)
//       cell.data.set(name, array)
//     } else {
//       cell.data.set(name, value)
//     }
//   })
//   $('.properties-container form input[type=checkbox]').each(function() {
//     if (this.value === 'transmit-variable') {
//       if ($(this).is(':checked')) {
//         editor.graph.setCellStyles('dashed', undefined, [cell])
//       } else {
//         editor.graph.setCellStyles('dashed', 1, [cell])
//       }
//     }
//     cell.data.set(this.value, $(this).is(':checked') ? '1' : '0')
//   })
//   cell.data.set('shape', shape)
// }

// TODO:
// function resizeSlideBar() {
//   var $dom = $('.sidebar-container')
//   var height = $dom.height()
//   var len = $dom.find('img').length
//   var totalHeight = len * 46
//   var w = Math.ceil(totalHeight / height)
//   $dom.width(w * 50)
//   $('.editor-container,.xml-container').css('left', w * 50 + 'px')
// }

function validXML(callback) {
  var cell = editor.valid()
  if (cell) {
    layui.layer.confirm('检测到有箭头未连接到节点上，是否处理？', {
      title: '异常处理',
      btn: ['处理', '忽略']
    }, function(index) {
      layui.layer.close(index)
      editor.selectCell(cell)
    }, function() {
      callback && callback()
    })
  } else {
    callback && callback()
  }
}

// TODO: 页面加载开始执行的逻辑
$(function() {
  $.ajax({
    url: 'spider/other',
    type: 'post',
    data: {
      id: getQueryString('id')
    },
    dataType: 'json',
    success: function(others) {
      flows = others
    }
  })

  $.ctrl = function(key, callback, args) {
    var isCtrl = false
    $(document).keydown(function(e) {
      if (!args) args = []
      if (e.keyCode === 17) isCtrl = true
      if (e.keyCode === key.charCodeAt(0) && isCtrl) {
        callback.apply(this, args)
        isCtrl = false
        return false
      }
    }).keyup(function(e) {
      if (e.keyCode === 17) isCtrl = false
    })
  }

  $.ctrl('S', function() {
    $('input,textarea').blur()
    Save()
  })

  $.ctrl('Q', function() {
    $('input,textarea').blur()
    $('.btn-test').click()
  })

  // TODO: 重新设置大小
  // resizeSlideBar()

  var templateCache = {}
  function loadTemplate(cell, model, callback) {
    serializeForm()
    var cells = model.cells
    var template = cell.data.get('shape') || 'root'
    if (cell.isEdge()) {
      template = 'edge'
    }
    var v = version
    var render = function() {
      layui.laytpl(templateCache[template]).render({
        data: cell.data,
        value: cell.value,
        flows: flows || [],
        model: model,
        cell: cell
      }, function(html) {
        $('.properties-container').attr('data-version', v).html(html).attr('data-cellid', cell.id)
        layui.form.render()
        renderCodeMirror()
        resizeSlideBar()
        callback && callback()
      })
    }
    if (templateCache[template]) {
      render()
      return
    }
    $.ajax({
      url: 'resources/templates/' + template + '.html',
      async: false,
      success: function(content) {
        templateCache[template] = content
        render()
      }
    })
  }
  if (!mxClient.isBrowserSupported()) {
    layui.layer.msg('浏览器不支持!!')
  } else {
    editor = new SpiderEditor({
      element: $('.editor-container')[0],
      selectedCellListener: function(cell) {	// 选中节点后打开属性面板
        loadTemplate(cell, editor.getModel(), serializeForm)
      }
    })

    // 绑定工具条点击事件
    bindToolbarClickAction(editor)
    // 加载图形
    loadShapes(editor, $('.sidebar-container')[0])
    layui.form.on('checkbox', function(e) {
      serializeForm()
    })
    layui.table.on('tool', function(obj) {
      layui.layer.confirm('您确定要删除吗？', {
        title: '删除'
      }, function(index) {
        obj.del()
        serializeForm()
        renderCodeMirror()
        layui.layer.close(index)
      })
    })

    // 节点名称输入框事件
    $('body').on('mousewheel', '.layui-tab .layui-tab-title', function(e, delta) {
      var $dom = $(this)
      var wheel = e.originalEvent.wheelDelta || -e.originalEvent.detail
      var delta = Math.max(-1, Math.min(1, wheel))
      e.preventDefault = function() {}
      if (delta > 0) {
        $dom.scrollLeft($dom.scrollLeft() - 60)
      } else {
        $dom.scrollLeft($dom.scrollLeft() + 60)
      }
      return false
    }).on('dblclick', '.layui-input-block[codemirror]', function() {
      if ($(this).parent().hasClass('layui-layer-content')) {
        return
      }
      layui.layer.open({
        type: 1,
        title: '请输入' + $(this).prev().html() + '表达式',
        content: $(this),
        skin: 'codemirror',
        area: '800px'
      })
    }).on('blur', 'input,textarea', function() {
      serializeForm()
    }).on('click', '.history-version li', function() {
      var timestamp = $(this).data('timestamp')
      layui.layer.confirm('你确定要恢复到该版本吗？', function(index) {
        layui.layer.close(index)
        var layerIndex = layui.layer.load(1)
        $.ajax({
          url: 'spider/history',
          data: {
            id: id,
            timestamp: timestamp
          },
          success: function(data) {
            if (data.code === 1) {
              version = timestamp
              editor.setXML(data.data)
              layui.layer.close(layerIndex)
              layui.layer.msg('恢复成功')
            } else {
              layui.layer.msg(data.message)
            }
          }
        })
      })
    }).on('click', '.btn-history', function() {
      $.ajax({
        url: 'spider/history',
        data: {
          id: id
        },
        success: function(data) {
          if (data.code === 1) {
            if (data.data.length > 0) {
              var array = []
              for (var i = data.data.length - 1; i >= 0; i--) {
                var timestamp = Number(data.data[i])
                array.push({
                  time: new Date(timestamp).format('yyyy-MM-dd hh:mm:ss'),
                  timestamp: timestamp
                })
              }
              layui.laytpl($('#history-version-tmpl').html()).render(array, function(html) {
                layui.layer.open({
                  type: 1,
                  title: '历史版本',
                  id: 'history-revert',
                  shade: 0,
                  resize: false,
                  content: html,
                  offset: 'rt'
                })
              })
            } else {
              layui.layer.msg('暂无历史版本！')
            }
          } else {
            layui.layer.msg(data.message)
          }
        }
      })
    }).on('click', '.table-row-add', function() {	// 添加一行
      serializeForm()
      var tableId = $(this).attr('for')
      var $table = $('#' + tableId)
      var cellId = $table.data('cell')
      var data = getCellData(cellId, $table.data('keys').split(','))
      data.push({})
      layui.table.reload(tableId, {
        data: data
      })
      renderCodeMirror()
    }).on('click', '.table-row-up', function() {	// 上移
      var current = $(this).parent().parent().parent() // 获取当前<tr>
      var prev = current.prev() // 获取当前<tr>前一个元素
      if (current.index() > 0) {
        current.insertBefore(prev) // 插入到当前<tr>前一个元素前
        serializeForm()
      }
      renderCodeMirror()
    }).on('click', '.table-row-down', function() {	// 下移
      var current = $(this).parent().parent().parent() // 获取当前<tr>
      var next = current.next() // 获取当前<tr>后面一个元素
      if (next) {
        current.insertAfter(next) // 插入到当前<tr>后面一个元素后面
        serializeForm()
      }
      renderCodeMirror()
    }).on('click', '.editor-form-node .function-remove,.editor-form-node .cmd-remove', function() {
      var $dom = $(this).parents('.draggable')
      $dom.remove()
      serializeForm()
    }).on('click', '.editor-form-node .cookie-batch', function() {
      var tableId = $(this).attr('for')
      var $table = $('#' + tableId)
      var cellId = $table.data('cell')
      var data = getCellData(cellId, $table.data('keys').split(','))
      layui.layer.open({
        type: 1,
        title: '请输入Cookie',
        content: `<textarea id="cookies" name="cookies" placeholder="请输入Cookies，分号( ; )分隔Cookie，等于号( = )分隔name和value" autocomplete="off" class="layui-textarea"  lay-verify="required" style="height:250px"></textarea>`,
        area: '800px',
        btn: ['关闭', '设置'],
        btn2: function() {
          var cookieStr = $('#cookies').val()
          var cookieArr = cookieStr.split(';')
          var length = $('.draggable').length
          serializeForm()
          for (var i = 0; i < cookieArr.length; i++) {
            var cookieItem = cookieArr[i]
            var index = cookieItem.indexOf('=')
            if (index < 0) {
              layer.alert('cookie数据格式错误')
              appendFlag = false
              return
            } else {
              data.push({
                'cookie-name': $.trim(cookieItem.substring(0, index)),
                'cookie-value': $.trim(cookieItem.substring(index + 1))
              })
            }
          }
          layui.table.reload(tableId, {
            data: data
          })
          renderCodeMirror()
          serializeForm()
        }
      })
    }).on('click', '.editor-form-node .header-batch', function() {
      var tableId = $(this).attr('for')
      var $table = $('#' + tableId)
      var cellId = $table.data('cell')
      var data = getCellData(cellId, $table.data('keys').split(','))
      layui.layer.open({
        type: 1,
        title: '请输入Header',
        content: `<textarea id="headers" name="headers" placeholder="请输入Headers，一行一个，冒号( : )分割name和value" autocomplete="off" class="layui-textarea"  lay-verify="required" style="height:250px"></textarea>`,
        area: '800px',
        btn: ['关闭', '设置'],
        btn2: function() {
          var headerStr = $('#headers').val()
          var headerArr = headerStr.split('\n')
          var length = $('.draggable').length
          for (var i = 0; i < headerArr.length; i++) {
            var headerItem = headerArr[i]
            var index = headerItem.indexOf(':')
            if (index < 0) {
              layer.alert('header数据格式错误')
              return
            } else {
              data.push({
                'header-name': $.trim(headerItem.substring(0, index)),
                'header-value': $.trim(headerItem.substring(index + 1))
              })
            }
          }
          layui.table.reload(tableId, {
            data: data
          })
          renderCodeMirror()
          serializeForm()
        }
      })
    }).on('click', '.editor-form-node .parameter-batch', function() {
      var tableId = $(this).attr('for')
      var $table = $('#' + tableId)
      var cellId = $table.data('cell')
      var data = getCellData(cellId, $table.data('keys').split(','))
      layui.layer.open({
        type: 1,
        title: '请输入参数',
        content: `<textarea id="paramters" name="paramters" placeholder="请输入参数，一行一个，冒号( : )、等号（ = ）、空格（  ）或tab（ \t ）分割name和value" autocomplete="off" class="layui-textarea"  lay-verify="required" style="height:250px"></textarea>`,
        area: '800px',
        btn: ['关闭', '设置'],
        btn2: function() {
          var paramterStr = $('#paramters').val()
          var paramterArr = paramterStr.split('\n')
          var length = $('.draggable').length
          for (var i = 0; i < paramterArr.length; i++) {
            var paramterItem = paramterArr[i]
            var index = -1
            var indexArr = []
            indexArr.push(paramterItem.indexOf(':'))
            indexArr.push(paramterItem.indexOf('='))
            indexArr.push(paramterItem.indexOf(' '))
            indexArr.push(paramterItem.indexOf('\t'))
            for (var j = 0; j < indexArr.length; j++) {
              if (indexArr[j] >= 0) {
                if (index < 0) {
                  index = indexArr[j]
                }
                index = Math.min(index, indexArr[j])
              }
            }
            if (index < 0) {
              layer.alert('参数数据格式错误')
              return
            } else {
              data.push({
                'parameter-name': $.trim(paramterItem.substring(0, index)),
                'parameter-value': $.trim(paramterItem.substring(index + 1))
              })
            }
          }
          layui.table.reload(tableId, {
            data: data
          })
          renderCodeMirror()
          serializeForm()
        }
      })
    }).on('click', '.editor-form-node .function-add', function() {
      var index = $('.draggable').length
      $(this).parent().parent().before('<div id="function' + index + '" class="draggable" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)"><div class="layui-form-item layui-form-relative"><i class="layui-icon layui-icon-close function-remove"></i><label class="layui-form-label">执行函数</label><div class="layui-input-block array" codemirror="function" placeholder="执行函数"></div></div></div>')
      renderCodeMirror()
    }).on('click', '.editor-form-node .cmd-add', function() {
      var index = $('.draggable').length
      $(this).parent().parent().before('<div id="' + index + '" class="draggable" draggable="true" ondragstart="drag(event)" ondrop="drop(event)" ondragover="allowDrop(event)"><div class="layui-form-item layui-form-relative"><i class="layui-icon layui-icon-close cmd-remove"></i><label class="layui-form-label">执行命令</label><div class="layui-input-block array" codemirror="cmd" placeholder="执行命令"></div></div></div>')
      renderCodeMirror()
    })

    layui.form.on('select(bodyType)', function(e) {
      var bodyType = $(e.elem).val()
      $('.form-body-raw,.form-body-form-data').hide()
      if (bodyType === 'raw') {
        $('.form-body-raw').show()
      }
      if (bodyType === 'form-data') {
        $('.form-body-form-data').show()
      }
      renderCodeMirror()
      serializeForm()
    })

    layui.form.on('select(targetCheck)', function(data) {
      var targetDiv = $(data.elem).attr('target-div')
      var targetValue = $(data.elem).attr('target-value')
      if (targetDiv != null) {
        if (data.elem.value === targetValue) {
          $('.' + targetDiv).show()
        } else {
          $('.' + targetDiv).hide()
        }
      }
    })

    layui.form.on('checkbox(targetCheck)', function(data) {
      var targetDiv = $(data.elem).attr('target-div')
      if (targetDiv != null) {
        if (data.elem.checked) {
          $('.' + targetDiv).show()
        } else {
          $('.' + targetDiv).hide()
        }
      }
    })
    layui.element.on('tab', function() {
      for (var key in codeMirrorInstances) {
        codeMirrorInstances[key].refresh()
      }
    })
    layui.form.on('select', serializeForm)
    var id = getQueryString('id')
    if (id != null) {
      $.ajax({
        url: 'spider/xml',
        async: false,
        data: {
          id: id
        },
        success: function(xml) {
          editor.setXML(xml)
        }
      })
      // editor.importFromUrl('spider/xml?id=' +  id)
    }
    editor.onSelectedCell()
  }

  /**
   * 加载各种图形
   */
  function loadShapes(editor, container) {
    // 定义图形
    var shapes = [{
      name: 'start',
      title: '开始',
      image: '',
      hidden: true,
      defaultAdd: true
    }, {
      name: 'request',
      image: '',
      title: '开始抓取',
      desc: '抓取静态HTML页面或者API接口，抓取结果存为resp变量中。<br/>支持方法参考命令提示。'
    }, {
      name: 'variable',
      image: '',
      title: '定义变量',
      desc: '定义流程变量。<br/>定义变量有先后顺序，先定义变量后续可以使用，拖动可以交换变量顺序。'
    }, {
      name: 'loop',
      image: '',
      title: '循环',
      desc: ''
    }, {
      name: 'forkJoin',
      image: '',
      title: '执行结束',
      desc: ''
    }, {
      name: 'comment',
      image: '',
      title: '注释',
      desc: '仅仅是注释,毫无作用'
    }, {
      name: 'output',
      image: '',
      title: '输出',
      desc: '输出流程中的变量结果（仅测试下有用）'
    }, {
      name: 'executeSql',
      image: '',
      title: '执行SQL',
      desc: '执行sql，需配置数据源，sql执行结果存于变量rs中。<br/>语句类型为：select，返回:List&lt;Map&lt;String,Object&gt;&gt;<br/>语句类型为：selectOne，返回:Map&lt;String,Object&gt;<br/>语句类型为：selectInt，返回:Integer<br/>语句类型为：insert、update、delete，返回:int，批量操作返回int数组<br/>sql中变量必须用 # # 包裹，如：#${title}#'
    }, {
      name: 'script',
      image: '',
      title: '执行脚本',
      desc: '单独执行脚本方法'
    }, {
      name: 'function',
      image: '',
      title: '执行函数',
      desc: '单独执行函数方法，结果不保存为变量'
    }, {
      name: 'process',
      image: '',
      title: '子流程',
      desc: '执行其他spiderFlow流程，父子流程变量共享'
    }]

    var addShape = function(shape) {
      var image = new Image()
      image.src = shape.image
      image.title = shape.title
      image.id = shape.name
      image.onclick = function(ev) {
        if (shape.desc) {
          layer.tips('(' + shape.name + ')' + shape.title + '<hr/>' + shape.desc, '#' + shape.name, {
            tips: [1, '#3595CC'],
            area: ['auto', 'auto'],
            time: 4000
          })
        }
      }
      if (!shape.hidden) {
        container.appendChild(image)
      }

      if (!shape.disabled) {
        editor.addShape(shape.name, shape.title || 'Label', image, shape.defaultAdd)
      }
    }

    for (var i = 0, len = shapes.length; i < len; i++) {
      addShape(shapes[i])
    }

    $.ajax({
      url: 'spider/shapes',
      type: 'post',
      dataType: 'json',
      async: false,
      success: function(shapeExts) {
        for (var i = 0, len = shapeExts.length; i < len; i++) {
          var shape = shapeExts[i]
          addShape(shape)
          var image = new Image()
          image.src = shape.image
          image.title = shape.title
          editor.addShape(shape.name, shape.title || 'Label', image, false)
          resizeSlideBar()
        }
      }
    })
  }
})

/**
 * 绑定工具条点击事件
 */
function bindToolbarClickAction(editor) {
  $('.xml-container textarea').bind('input propertychange', function() {
    editor.setXML($(this).val())
    editor.onSelectedCell()
  })
  $('.toolbar-container').on('click', '.btn-delete', function() {
    editor.deleteSelectCells()
  }).on('click', '.btn-selectAll', function() {
    editor.execute('selectAll')
  }).on('click', '.btn-undo', function() {
    editor.execute('undo')
  }).on('click', '.btn-redo', function() {
    editor.execute('redo')
  }).on('click', '.btn-cut', function() {
    editor.execute('cut')
  }).on('click', '.btn-copy', function() {
    editor.execute('copy')
  }).on('click', '.btn-paste', function() {
    editor.execute('paste')
  }).on('click', '.btn-console-xml', function() {
    console.log(editor.getXML())
  }).on('click', '.btn-edit-xml', function() {
    $('.editor-container').hide()
    $('.xml-container textarea').val(editor.getXML())
    $('.xml-container').show()
    $(this).removeClass('btn-edit-xml').addClass('btn-graphical-xml')
  }).on('click', '.btn-graphical-xml', function() {
    $('.editor-container').show()
    $('.xml-container').hide()
    $(this).removeClass('btn-graphical-xml').addClass('btn-edit-xml')
    //		editor.setXML($(".xml-container textarea").val());
    //		editor.onSelectedCell();
  }).on('click', '.btn-stop:not(.disabled)', function() {
    socket.send(JSON.stringify({
      eventType: 'stop'
    }))
  }).on('click', '.btn-resume:not(.disabled)', function() {
    $(this).addClass('disabled')
    $('.spiderflow-debug-tooltip').remove()
    socket.send(JSON.stringify({
      eventType: 'resume'
    }))
  }).on('click', '.btn-test', function() {
    runSpider(false)
  }).on('click', '.btn-debug', function() {
    runSpider(true)
  }).on('click', '.btn-return', function() {
    parent.openTab('爬虫列表', 'welcome', 'spiderList.html')
  }).on('click', '.btn-save', function() {
    Save()
  }).on('click', '.btn-dock-right', function() {
    $('.main-container').addClass('right')
    $('.main-container .properties-container').width('40%')
    $('.main-container .resize-container').attr('style', 'top:0px;left:auto;')
    layui.table.resize('spider-variable')
    var resize = $('.resize-container')[0]
    resize.onmousedown = function(e) {
      var startX = e.clientX
      resize.left = resize.offsetLeft
      var box = $('body')[0]
      document.onmousemove = function(e) {
        layui.table.resize('spider-variable')
        var endX = e.clientX
        var moveLen = resize.left + (endX - startX)
        var maxT = box.clientWidth - resize.offsetWidth
        if (moveLen < 150) moveLen = 150
        if (moveLen > maxT - 150) moveLen = maxT - 150
        if (box.clientWidth - moveLen < 400 || box.clientWidth - moveLen > 800) {
          return
        }
        resize.style.left = moveLen + 'px'
        $('.editor-container').css('right', ($('body').width() - moveLen) + 'px')
        $('.properties-container').width(box.clientWidth - moveLen - 5)
        $('.xml-container').width($('.main-container').width() - $('.properties-container').width() - $('.sidebar-container').width() + 8)
      }
      document.onmouseup = function(evt) {
        document.onmousemove = null
        document.onmouseup = null
        resize.releaseCapture && resize.releaseCapture()
      }
      resize.setCapture && resize.setCapture()
      return false
    }
  }).on('click', '.btn-dock-bottom', function() {
    resizeSlideBar()
    $('.main-container').removeClass('right')
    $('.properties-container').height(200).width('100%')
    $('.sidebar-container').css('bottom', '200px')
    $('.editor-container').css('bottom', '200px')
    $('.main-container .resize-container').attr('style', 'left:0px;top:auto;bottom:190px')
    var resize = $('.resize-container')[0]
    resize.onmousedown = function(e) {
      var startY = e.clientY
      resize.top = resize.offsetTop
      var box = $('body')[0]
      var maxT = box.clientHeight
      document.onmousemove = function(e) {
        var moveLen = e.clientY
        if (moveLen < 250) moveLen = 250
        if (moveLen > maxT - 150) moveLen = maxT - 150
        resize.style.top = moveLen + 'px'
        resizeSlideBar()
        $('.editor-container,.sidebar-container,.xml-container').css('bottom', ($('body').height() - moveLen) + 'px')
        $('.properties-container').height(box.clientHeight - moveLen - 5)
      }
      document.onmouseup = function(evt) {
        document.onmousemove = null
        document.onmouseup = null
        resize.releaseCapture && resize.releaseCapture()
      }
      resize.setCapture && resize.setCapture()
      return false
    }
  })
  $('.btn-dock-bottom').click()
}
function runSpider(debug) {
  validXML(function() {
    $('.btn-debug,.btn-test,.btn-resume').addClass('disabled')
    $('.btn-stop').removeClass('disabled')
    var LogViewer
    var tableMap = {}
    var first = true
    var filterText = ''
    var testWindowIndex = layui.layer.open({
      id: 'test-window',
      type: 1,
      skin: 'layer-test',
      content: '<div class="test-window-container"><div class="output-container"><div class="layui-tab layui-tab-fixed layui-tab-brief"><ul class="layui-tab-title"></ul><div class="layui-tab-content"></div></div></div><canvas class="log-container" width="677" height="100"></canvas></div>',
      area: ['680px', '400px'],
      shade: 0,
      offset: 'rt',
      maxmin: true,
      maxWidth: 700,
      maxHeight: 400,
      title: '测试窗口',
      btn: ['关闭', '显示/隐藏输出', '显示/隐藏日志', '停止'],
      btn2: function() {
        var $output = $('.test-window-container .output-container')
        var $log = $('.test-window-container .log-container')
        if ($output.is(':hidden')) {
          $output.show()
          $output.find('canvas').each(function() {
            if ($log.is(':hidden')) {
              this.height = 290
            } else {
              this.height = 200
            }
          })
          $log.attr('height', 100)
          LogViewer.resize()
          for (var tableId in tableMap) {
            tableMap[tableId].instance.resize()
          }
        } else {
          $output.hide()
          $log.attr('height', 320)
          LogViewer.resize()
          for (var tableId in tableMap) {
            tableMap[tableId].instance.resize()
          }
        }
        return false
      },
      btn3: function() {
        var $output = $('.test-window-container .output-container')
        var $log = $('.test-window-container .log-container')
        if ($log.is(':hidden')) {
          $log.show()
          $log.attr('height', $output.is(':hidden') ? 320 : 100)
          $output.find('canvas').each(function() {
            this.height = 200
          })
          LogViewer.resize()
          for (var tableId in tableMap) {
            tableMap[tableId].instance.resize()
          }
        } else {
          $log.hide()
          $output.find('canvas').each(function() {
            this.height = 320
          })
          LogViewer.resize()
          for (var tableId in tableMap) {
            tableMap[tableId].instance.resize()
          }
        }
        return false
      },
      btn4: function() {
        var $btn = $('#layui-layer' + testWindowIndex).find('.layui-layer-btn3')
        if ($btn.html() == '停止') {
          socket.send(JSON.stringify({
            eventType: 'stop'
          }))
        } else {
          $('.btn-debug,.btn-test,.btn-resume').addClass('disabled')
          $('.btn-stop').removeClass('disabled')
          socket.send(JSON.stringify({
            eventType: debug ? 'debug' : 'test',
            message: editor.getXML()
          }))
          $btn.html('停止')
        }
        return false
      },
      end: function() {
        if (socket) {
          socket.close()
          $('.spiderflow-debug-tooltip').remove()
          $('.btn-stop,.btn-resume').addClass('disabled')
          $('.btn-test,.btn-debug').removeClass('disabled')
        }
        if (LogViewer) {
          LogViewer.destory()
        }
        for (var tableId in tableMap) {
          tableMap[tableId].instance.destory()
        }
      },
      success: function(layero, index) {
        var logElement = $('.test-window-container .log-container')[0]
        var colors = {
          'array': '#2a00ff',
          'object': '#2a00ff',
          'boolean': '#600100',
          'number': '#000E59'
        }
        LogViewer = new CanvasViewer({
          element: logElement,
          onClick: function(e) {
            onCanvasViewerClick(e, '日志')
          }
        })
        $(layero).find('.layui-layer-btn')
          .append('<div class="layui-inline"><input type="text" class="layui-input" placeholder="输入关键字过滤日志"/></div>')
          .on('keyup', 'input', function() {
            LogViewer.filter(this.value)
          })
        socket = createWebSocket({
          onopen: function() {
            socket.send(JSON.stringify({
              eventType: debug ? 'debug' : 'test',
              message: editor.getXML()
            }))
          },
          onmessage: function(e) {
            var event = JSON.parse(e.data)
            var eventType = event.eventType
            var message = event.message
            if (eventType == 'finish') {
              $('.spiderflow-debug-tooltip').remove()
              $('#layui-layer' + testWindowIndex).find('.layui-layer-btn3').html('重新开始')
              $('.btn-stop,.btn-resume').addClass('disabled')
              $('.btn-test,.btn-debug').removeClass('disabled')
            } else if (eventType == 'output') {
              var tableId = 'output-' + message.nodeId
              var $table = $('#' + tableId)
              if ($table.length == 0) {
                tableMap[tableId] = {
                  index: 0
                }
                var $tab = $('.test-window-container .output-container .layui-tab')
                var outputTitle = '输出-' + tableId
                var cell = editor.getModel().cells[message.nodeId]
                if (cell) {
                  outputTitle = cell.value
                }
                if (first) {
                  $tab.find('.layui-tab-title').append('<li  class="layui-this">' + outputTitle + '</li>')
                  $tab.find('.layui-tab-content').append('<div class="layui-tab-item layui-show" data-output="' + tableId + '"></div>')
                  first = false
                } else {
                  $tab.find('.layui-tab-title').append('<li>' + outputTitle + '</li>')
                  $tab.find('.layui-tab-content').append('<div class="layui-tab-item" data-output="' + tableId + '"></div>')
                }
                $table = $('<canvas width="677" height="200"/>').appendTo($('.test-window-container .output-container .layui-tab-item[data-output=' + tableId + ']'))
                $table.attr('id', tableId)
                tableMap[tableId].instance = new CanvasViewer({
                  element: document.getElementById(tableId),
                  grid: true,
                  header: true,
                  style: {
                    font: 'bold 13px Consolas'
                  },
                  onClick: function(e) {
                    onCanvasViewerClick(e, '表格')
                  }
                })
                var cols = []
                var texts = [new CanvasText({
                  text: '序号',
                  maxWidth: 100
                })]
                for (var i = 0, len = message.outputNames.length; i < len; i++) {
                  texts.push(new CanvasText({
                    text: message.outputNames[i],
                    maxWidth: 200,
                    click: true
                  }))
                }
                tableMap[tableId].instance.append(texts)
              }
              var texts = [new CanvasText({
                text: ++tableMap[tableId].index,
                maxWidth: 200,
                click: true
              })]
              for (var i = 0, len = message.outputNames.length; i < len; i++) {
                var displayText = message.values[i]
                var variableType = 'string'
                if (Array.isArray(displayText)) {
                  variableType = 'array'
                  displayText = JSON.stringify(displayText)
                } else {
                  variableType = typeof displayText
                  if (variableType == 'object') {
                    displayText = JSON.stringify(displayText)
                  }
                }
                texts.push(new CanvasText({
                  text: displayText,
                  maxWidth: 200,
                  color: colors[variableType] || 'black',
                  click: true
                }))
              }
              tableMap[tableId].instance.append(texts)
              tableMap[tableId].instance.scrollTo(-1)
            } else if (eventType == 'log') {
              var texts = []
              var defaultColor = message.level == 'error' ? 'red' : ''
              texts.push(new CanvasText({
                text: message.level,
                color: defaultColor
              }))
              texts.push(new CanvasText({
                text: event.timestamp,
                color: defaultColor
              }))
              var temp = message.message.split('{}')
              message.variables = message.variables || []
              for (var i = 0, len = temp.length; i < len; i++) {
                if (temp[i] !== '') {
                  texts.push(new CanvasText({
                    text: temp[i],
                    color: defaultColor
                  }))
                }
                var object = message.variables[i]
                if (object !== undefined) {
                  var variableType = ''
                  var displayText = object
                  if (Array.isArray(object)) {
                    variableType = 'array'
                    displayText = JSON.stringify(displayText)
                  } else {
                    variableType = typeof object
                    if (variableType === 'object') {
                      displayText = JSON.stringify(displayText)
                    }
                  }
                  texts.push(new CanvasText({
                    text: displayText,
                    maxWidth: 330,
                    color: colors[variableType] || '#025900',
                    click: true
                  }))
                }
              }
              LogViewer.append(texts)
              LogViewer.scrollTo(-1)
            } else if (eventType == 'debug') {
              $('.btn-resume').removeClass('disabled')
              var type = message.event
              editor.selectCell(editor.graph.model.cells[message.nodeId])
              var selector
              if (type == 'request-parameter') {
                $('.layui-tab-title li:eq(1)').click()
              }
              if (type == 'request-cookie') {
                $('.layui-tab-title li:eq(2)').click()
              }
              if (type == 'request-header') {
                $('.layui-tab-title li:eq(3)').click()
              }
              if (type == 'request-body') {
                $('.layui-tab-title li:eq(4)').click()
              }
              if (type == 'common' || type == 'request-parameter' || type == 'request-header' || type == 'request-cookie') {
                selector = '.layui-table-cell input[value=' + message.key + ']'
              }
              if ($(selector).length == 0) {
                selector = '.properties-container input[name=' + message.key + ']'
              }
              if ($(selector).length == 0) {
                selector = '.properties-container [codemirror=' + message.key + ']'
              }
              var o1 = $(selector).offset()
              var $parent = $('.properties-container')
              var o2 = $parent.offset()
              if (o1.top > o2.top + $parent.height()) {
                $parent[0].scrollTop = o1.top - o2.top
              }
              var msg = message.value
              var isJson = Array.isArray(msg) || typeof msg === 'object'
              if (!isJson) {
                var temp = document.createElement('div');
                (temp.textContent != null) ? (temp.textContent = msg) : (temp.innerText = msg)
                msg = temp.innerHTML
                temp = null
              }
              var content = '<div class="message-content" style="padding:10px;' + (isJson ? '' : 'font-weight: bold;font-family:Consolas;font-size:12px;') + '">' + (isJson ? '' : msg.replace(/\n/g, '<br>')).replace(/ /g, '&nbsp;').replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;') + '</div>'
              var tooltip = bindTooltip(content, selector)
              if (isJson) {
                var $dom = $(tooltip.dom).find('.message-content')
                jsonTree.create(msg, $dom[0])
              }
            }
          }
        })
      }
    })
  })
}
function bindTooltip(content, selector) {
  var dom = document.createElement('div')
  var $target = $(selector)
  var offset = $target.offset()
  dom.className = 'spiderflow-debug-tooltip'
  dom.style.bottom = ($('body').height() - offset.top) + 'px'
  dom.style.left = (offset.left + $target.width() / 2) + 'px'
  dom.innerHTML = '<div class="content">' + content + '</div>'
  document.body.appendChild(dom)
  $(selector).offset()
  return {
    dom: dom,
    close: function() {
      document.body.removeChild(dom)
    }
  }
}
// 最近点击打开的弹窗
var index
function onCanvasViewerClick(e, source) {
  var msg = e.text
  var json
  try {
    json = JSON.parse(msg)
    if (!(Array.isArray(json) || typeof json === 'object')) {
      json = null
    }
  // eslint-disable-next-line no-empty
  } catch (e) {}

  if (!json) {
    var temp = document.createElement('div');
    (temp.textContent != null) ? (temp.textContent = msg) : (temp.innerText = msg)
    msg = temp.innerHTML
    temp = null
  }
  layer.close(index)
  index = layer.open({
	  type: 1,
	  title: source + '内容',
	  content: '<div class="message-content" style="padding:10px;' + (json ? '' : 'font-weight: bold;font-family:Consolas;font-size:12px;') + '">' + (json ? '' : msg.replace(/\n/g, '<br>')).replace(/ /g, '&nbsp;').replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;') + '</div>',
	  shade: 0,
	  area: json ? ['700px', '500px'] : 'auto',
	  maxmin: true,
	  maxWidth: (json ? undefined : 700),
	  maxHeight: (json ? undefined : 400),
	  success: function(dom, index) {
		 var $dom = $(dom).find('.message-content')
		 if (json) {
			 jsonTree.create(json, $dom[0])
		 }
	  }
  })
}
function createWebSocket(options) {
  options = options || {}
  var socket
  if (location.host === 'demo.spiderflow.org') {
    socket = new WebSocket(options.url || 'ws://49.233.182.130:8088/ws')
  } else {
    socket = new WebSocket(options.url || (location.origin.replace('http', 'ws') + '/ws'))
  }
  socket.onopen = options.onopen
  socket.onmessage = options.onmessage
  socket.onerror = options.onerror || function() {
    layer.layer.msg('WebSocket错误')
  }
  return socket
}

var flowId
function Save() {
  validXML(function() {
    $.ajax({
      url: 'spider/save',
      type: 'post',
      data: {
        id: getQueryString('id') || flowId,
        xml: editor.getXML(),
        name: editor.graph.getModel().getRoot().data.get('spiderName') || '未定义名称'
      },
      success: function(id) {
        flowId = id
        layui.layer.msg('保存成功', {
          time: 800
        }, function() {
          // location.href = "spiderList.html";
        })
      }
    })
  })
}

function allowDrop(ev) {
  ev.preventDefault()
}

function drag(ev) {
  ev.dataTransfer.setData('moverTarget', ev.target.id)
}

function drop(ev) {
  var moverTargetId = ev.dataTransfer.getData('moverTarget')
  $(ev.target).parents('.draggable').before($('#' + moverTargetId))
  ev.preventDefault()
  serializeForm()
}
